#include "canif.h"
#include <QDebug>

uint64_t getustimestamp(void);

CanIF::CanIF()
{
    //    qRegisterMetaType<sCANObj>("CAN_OBJ");
    //    qRegisterMetaType<sCANObj>("CAN_OBJ&");

    //    hRxEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    //    ResetEvent(m_SortEvent);
    uMask = 0xFFFFFFFF;
    uAck = 0;
    pCanDrv = 0;
    uCh = 0;
    ResetTimeStamp();
}

CanIF::~CanIF()
{
    if (bRun)
    {
        bRun = false;
        QThread::msleep(200);
    }
    if (pCanDrv)
    {
        delete pCanDrv;
        pCanDrv = 0;
    }
}

void CanIF::ResetTimeStamp(void)
{
    memset(&startTime, 0, sizeof(startTime));
    GetSystemTime(&startTime);
    uStartTimeStamp = getustimestamp();
}

VPort *CanIF::CreateVPort(uint32_t Ack, uint32_t Mask)
{
    VPort *m_pPort = new VPort(this);
    m_pPort->SetMask(Mask, Ack);
    portArray.append(m_pPort);
    return m_pPort;
}

bool CanIF::DelVPort(VPort *pPort)
{
    for (int i = 0; i < portArray.size(); i++)
    {
        if (portArray.at(i) == pPort)
        {
            portArray.remove(i);
            delete (pPort);
            break;
        }
    }
    return 0;
}

bool CanIF::PortRead(VPort *pPort, const sCANObj &msg, int WaitTime)
{
    if (pPort == 0)
        return false;
    /*
        if(!pPort->mRxBuf.isEmpty()){
            msg = pPort->mRxBuf.first();
            pPort->mRxBuf.removeFirst();
            return true;
        }else{
            WaitForSingleObject(pPort->hRxEvent,WaitTime);

        }
    */
    return 0;
}

bool CanIF::OpenDevice(QString sMakers, QString sDevName, const sCanBaud mBaud)
{
    if (bRun == true)
        CloseDevice();

    if (sMakers == "Vector")
    {
        pCanDrv = new VectorCan();
    }
    // else if (sMakers == "PEAK")
    // {
    //     pCanDrv = new PeakCan();
    // }
    else if (sMakers == "ZLG")
    {
        pCanDrv = new ZlgCan();
    }
    else
    {
        return false;
    }

    if (pCanDrv)
    {
        if (pCanDrv->OpenDevice(sDevName, mBaud))
        {
            bRun = true;
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
}

bool CanIF::CloseDevice()
{
    bRun = false;
    QThread::msleep(200);

    if (pCanDrv == 0)
        return false;

    pCanDrv->CloseDevice();
    delete pCanDrv;
    pCanDrv = 0;

    return true;
}

int gettimeofday(struct timeval *tp, void *tzp)
{
    uint64_t intervals;
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);
    /*
     * A file time is a 64-bit value that represents the number
     * of 100-nanosecond intervals that have elapsed since
     * January 1, 1601 12:00 A.M. UTC.
     *
     * Between January 1, 1970 (Epoch) and January 1, 1601 there were
     * 134744 days,
     * 11644473600 seconds or
     * 11644473600,000,000,0 100-nanosecond intervals.
     *
     * See also MSKB Q167296.
     */
    intervals = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
    intervals -= 116444736000000000;
    tp->tv_sec = (long)(intervals / 10, 000, 000);
    tp->tv_usec = (long)(intervals / 10);
    return (0);
}

uint64_t getustimestamp(void)
{
    uint64_t intervals;
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);
    intervals = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
    intervals -= 116444736000000000;
    return intervals / 10;
}

void CanIF::Send(const sCANObj &msg)
{
    if (pCanDrv == 0)
        return;
    sCANObj msgfb = msg;
    msgfb.bDir = 1;
    msgfb.uCh = 0;
    msgfb.TimeStamp = getustimestamp() - uStartTimeStamp;
    uTxCnt = 0;
    do
    {
        if (pCanDrv->Send(msgfb))
        {
            // emit receive(msgfb); // 发送消息到窗口
            break;
        }
        uTxCnt++;

    } while (uTxCnt < 5);

    if (5 == uTxCnt)
    {
        qDebug()<<"CAN Send失败:"<<uTxCnt;
    }
}

void CanIF::run()
{
    sCANObj msg;
    // QDateTime time;

    qDebug()<<"子线程功能函数ID:"<<QThread::currentThreadId();

    // while (bRun)
    while (true)
    {
        // QThread::msleep(100);
        // uRxCnt++;

        if (pCanDrv == 0)
        {
            QThread::msleep(100);
            continue;
        }

        if (pCanDrv->Receive(msg, 100))
        {
            //            time = QDateTime::currentDateTime();
            msg.TimeStamp = getustimestamp() - uStartTimeStamp;
            emit receive(msg); // 发送消息到窗口
            int cnt = portArray.count();
            for (int i = 0; i < cnt; i++)
            {
                if (portArray[i]->Mask(msg.ID))
                {
                    portArray[i]->MsgIn(msg);
                }
            }
            // uRxCnt++;
            emit updateLabelText(msg);
        }
    }
}

VPort::VPort(CanIF *pDev)
{
    pDevice = 0;
    hRxEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    ResetEvent(hRxEvent);
    pDevice = pDev;
}

bool VPort::ClearBuffer(void)
{
    mRxBuf.clear();
    return true;
}

bool VPort::Transmit(const sCANObj &canmsg)
{
    if (pDevice)
    {
        pDevice->Send(canmsg);
    }
    return true;
}

bool VPort::MsgIn(const sCANObj &msg)
{
    mutex.lock();
    mRxBuf.append(msg);
    if (mRxBuf.count() > 1000)
        mRxBuf.removeFirst();
    mutex.unlock();

    SetEvent(hRxEvent);

    return true;
}

bool VPort::Receive(sCANObj *canmsg, int WaitTime)
{
    ResetEvent(hRxEvent);
    if (mRxBuf.count() > 0)
    {
        mutex.lock();
        *canmsg = mRxBuf.head();
        mRxBuf.removeFirst();
        mutex.unlock();
        return true;
    }
    else
    {
        DWORD dw = WaitForSingleObject(hRxEvent, WaitTime);
        if (dw == WAIT_OBJECT_0)
        {
            mutex.lock();
            if (mRxBuf.count() > 0)
            {
                *canmsg = mRxBuf.head();
                mRxBuf.removeFirst();
            }
            mutex.unlock();
            return true;
        }
        else if (dw == WAIT_FAILED)
        {
            return false;
        }
        else
        {
            ResetEvent(hRxEvent);
        }
    }
    return false;
}

bool VPort::Mask(uint32_t uId)
{
    if ((uId & uMask) == (uAck & uMask))
        return true;
    else
        return false;
}
